Recipe Read Guard Metadata Command Remediation#4213
Merged
Conversation
Reviewer findings addressed:
* is_allowed_protected_path_metadata_command (status branch): drop
redundant flag.startswith('-v') check — _GIT_STATUS_CONTENT_FLAGS
already enumerates {-v, --verbose}, so the prefix check only added
future-flag fragility (any hypothetical -vX would have been caught
but should not be).
* is_allowed_protected_path_metadata_command (wc branch): match the
git branch's verb.endswith('/git') pattern with verb.endswith('/wc')
so /usr/bin/wc and command wc are recognized symmetrically.
* wc branch flag allowlist: replace the confusing
'token == --lines or ('l' in token and not set(token.lstrip('-')) -
{"l"})' expression with a single _is_allowed_wc_flag() helper
driven by a re.fullmatch pattern (-l+|--lines). Reject
--lines=10 (value-bearing) and -lL (compound) cleanly while keeping
-l, -ll, --lines allowlisted.
The deny parametrize case had an unbalanced open-paren without a close. The shlex tokenizer raises ValueError on unbalanced input and returns an empty list, so the deny assertion only passed because the raw substitution regex fires on the literal string before shlex parse. A future refactor that normalizes via shlex first would silently flip behavior on this input. Closing the paren and quoting the substitution makes the input reflect a realistic adversarial payload while still exercising the same substitution branch.
Two server-test improvements from the review: * test_allows_recipe_path_vcs_and_metadata_commands: in addition to checking success=True, assert that subtype is absent and the recipe-read deny trigger is not present in result. A guard that always returns allow (i.e. never triggers the deny path) would also pass the success-only check, so the negative assertions catch a false-negative regression. * test_denies_protected_path_content_bypasses: annotate the substitution and state-var cases with the bypass class they target so a future parser refactor knows to preserve these specific deny paths.
The previous commit added 'assert RECIPE_READ_DENY_TRIGGER not in result["result"]' but successful run_cmd results do not contain a 'result' key — that key is only present on the deny path. The subtype-absent check is sufficient to distinguish a true positive (allowed through the metadata-allow path) from a false negative (allowed because the deny path silently bypassed).
The 'git add' content-flag allowlist omitted -A, --all, --force, --no-ignore-removal, and --no-all. 'git add -A -- src/.../foo.yaml' was therefore classified as metadata (allowed) but actually stages content for indirect read via 'git diff --staged'.
'is_allowed_protected_path_metadata_command' (and the broader git/wc/verb detection helpers) denied legitimate metadata commands prefixed with 'command', 'nice', 'time', 'sudo', 'nohup', 'timeout N', and 'stdbuf -o0' because '_command_start_index' only stripped 'env' and env-var assignments. The wrapper token became the verb and the real command (git/wc/...) was never inspected. 'xargs' is intentionally excluded: adding it would let 'xargs cat src/.../foo.yaml' reach the downstream reader check, weakening the xargs-chain bypass detection (DISCUSS in iter_1).
…ands, and content-staging flags Add parametrize cases to test_denies_protected_path_content_bypasses: * cat < path (input redirection) * git show/log -p/blame/grep with protected path (reader subcommands not in the metadata allowlist) * git add -A / --all with a restricted path (stages content for indirect read via 'git diff --staged') Pairs with the _GIT_ADD_CONTENT_FLAGS expansion; locks the corrected behavior so a future regression that drops the new flags will fail the suite.
This was referenced Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix recipe read guard false positives by centralizing protected-path command classification for hook and server-side command execution. The change permits narrow non-content metadata/staging operations against protected recipe, skill, and agent paths while continuing to deny direct content reads and shell bypasses.
The implementation adds shared classification coverage for safe
git add, metadata-onlygit diff, safegit status, andwc -lusage, and blocks content-producing Git modes, heredocs, substitutions, variable reuse in command chains, and unsafe chained reads. Hook and MCPrun_cmdbehavior now use the same classifier and focused regression tests cover the allow and deny cases.Closes #4207
Implementation Plan
Plan file:
/home/talon/projects/generic_automation_mcp/.autoskillit/temp/codex-loop/4207-manual-remediation-plan.md🤖 Generated with Claude Code via AutoSkillit
Token Usage Summary
* Step used a non-Anthropic provider; caching behavior may differ.
Token Efficiency
Model Usage Breakdown